package loan.calc.service.base.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import loan.calc.config.BaseEnumConverter;
import loan.calc.constant.dict.YNEnum;
import loan.calc.constant.dict.base.DictEnum;
import loan.calc.datasource.UseDataSource;
import loan.calc.domain.DictDO;
import loan.calc.domain.DictItemDO;
import loan.calc.dto.DictDTO;
import loan.calc.mapper.DictMapper;
import loan.calc.service.base.DictItemService;
import loan.calc.service.base.DictService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author v_ECD963
 * 针对表【dict(数据字典表)】的数据库操作Service实现
 * 2023-04-07 15:24:47
 */
@Service
@RequiredArgsConstructor
public class DictServiceImpl extends ServiceImpl<DictMapper, DictDO> implements DictService, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private final DictItemService dictItemService;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refreshDbSystemDict() throws ClassNotFoundException {
        List<DictDTO> systemDictList = loadSystemDictList();
        List<DictDO> dbSystemDictList = Db.lambdaQuery(DictDO.class).eq(DictDO::getSystemDict, YNEnum.Y).list();
        if (dbSystemDictList!=null && !dbSystemDictList.isEmpty()) {
            Db.lambdaUpdate(DictItemDO.class).in(DictItemDO::getDictValue, dbSystemDictList.stream().map(DictDO::getValue).collect(Collectors.toList()))
                    .remove();
            Db.removeByIds(dbSystemDictList, DictDO.class);
        }
        Db.saveBatch(systemDictList);
        Db.saveBatch(systemDictList.stream().flatMap(i -> i.getDictItemList().stream()).collect(Collectors.toList()));
    }

    @Override
    //@UseDataSource("slave")
    public List<DictDTO> loadAllDict() {
        List<DictDO> list = super.list();
        return list.stream().parallel().map(dict -> {
            DictDTO dictDTO = new DictDTO();
            BeanUtils.copyProperties(dict, dictDTO);
            dictDTO.setDictItemList(dictItemService.listByDictValue(dict.getValue()));
            return dictDTO;
        }).collect(Collectors.toList());
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private List<DictDTO> loadSystemDictList() throws ClassNotFoundException {
        List<DictDTO> dictList = new ArrayList<>();
        for (Object bootBean : this.applicationContext.getBeansWithAnnotation(SpringBootApplication.class).values()) {
            ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
            scanner.addIncludeFilter(new AnnotationTypeFilter(DictEnum.class));
            for (BeanDefinition beanDefinition : scanner.findCandidateComponents(bootBean.getClass().getPackage().getName())) {
                Class<Enum> clazz = (Class<Enum>) Class.forName(beanDefinition.getBeanClassName());
                DictEnum dictEnum = clazz.getAnnotation(DictEnum.class);
                DictDTO dict = new DictDTO();
                dict.setValue(dictEnum.value());
                dict.setDescription(dictEnum.description());
                dict.setSystemDict(YNEnum.Y);
                for (Object enumConstant : clazz.getEnumConstants()) {
                    DictItemDO dictItem = new DictItemDO();
                    dictItem.setDictValue(dictEnum.value());
                    Field valueField = BaseEnumConverter.getValueField(clazz);
                    Field descField = BaseEnumConverter.getDescField(clazz);
                    ReflectionUtils.makeAccessible(valueField);
                    ReflectionUtils.makeAccessible(descField);
                    dictItem.setValue((String) ReflectionUtils.getField(valueField, enumConstant));
                    dictItem.setDescription((String) ReflectionUtils.getField(descField, enumConstant));
                    dict.addDictItem(dictItem);
                }
                dictList.add(dict);
            }
        }
        return dictList;
    }
}
